package com.jmxcfc.blfsc.jiwei.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jmxcfc.blfsc.common.sms.SmsConstants;
import com.jmxcfc.blfsc.common.common.request.BaseRequest;
import com.jmxcfc.blfsc.common.common.response.BaseResponse;
import com.jmxcfc.blfsc.common.exception.*;
import com.jmxcfc.blfsc.common.sms.BaseContext;
import com.jmxcfc.blfsc.common.sms.SmsRequest;
import com.jmxcfc.blfsc.common.common.enums.ProcessStateEnum;
import com.jmxcfc.blfsc.common.sms.extend.SmsExtend;
import com.jmxcfc.blfsc.common.validation.rule.RequestFieldRule;
import com.jmxcfc.blfsc.common.validation.rule.SmsRulesUtil;
import com.jmxcfc.blfsc.jiwei.common.Constants;
import com.jmxcfc.blfsc.jiwei.config.JiweiProperties;
import com.jmxcfc.blfsc.jiwei.entity.SendDetailSub;
import com.jmxcfc.blfsc.jiwei.service.ISendDetailSubService;
import com.jmxcfc.blfsc.jiwei.service.ISmsRequestService;
import com.jmxcfc.blfsc.jiwei.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.*;

/**
 * <p>
 * 短信记录表 服务实现类
 * </p>
 *
 * @author pbs
 * @since 2023-09-06 2:38:55
 */
@Slf4j
@Service
@RefreshScope
public class SmsRequestServiceImpl implements ISmsRequestService {

    private JiweiProperties jiweiProperties;
    private ISendDetailSubService sendDetailSubService;
    private RestTemplate restTemplate;


    @Autowired
    public void setJiweiProperties(JiweiProperties jiweiProperties) {
        this.jiweiProperties = jiweiProperties;
    }

    @Autowired
    public void setSendDetailSubService(ISendDetailSubService sendDetailSubService) {
        this.sendDetailSubService = sendDetailSubService;
    }

    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }


    /**
     * 发送短信
     * 校验:
     * requestNo(内部的请求流水号)不为空、
     * 批次号(批次号,对应接口集微参数transactionId字段)不为空、
     * sendDtl(发送集合)不为空
     * 扩展字段检验:
     * 账号、密码
     * @param requestMessage
     * @return
     */
    @Override
    public BaseResponse<JSONObject> sendRequest(BaseRequest<SmsRequest<SmsExtend>> requestMessage) {
        BaseContext<String> baseContext = new BaseContext<>();
        //头部参数校验规则:业务号、请求流水号字段(集微参数transactionId)
        List<RequestFieldRule<?>> headerElementRules = baseContext.getHeaderElementRules();
        headerElementRules.add(SmsRulesUtil.getRequestNoRule());
        headerElementRules.add(SmsRulesUtil.getSerialSeqRule("批次号字段(集微参数transactionId)规则检验不符合"));

        //请求体参数校验:发送集合
        List<RequestFieldRule<?>> bodyElementRules = baseContext.getBodyElementRules();
        bodyElementRules.add(SmsRulesUtil.getSendDtlRule());
        List<RequestFieldRule<?>> bodyExtendElementRules = baseContext.getBodyExtendElementRules();
        //请求体扩展字段校验:账号、密码
        bodyExtendElementRules.add(SmsRulesUtil.getAcctNolRule("账号字段规则检验不符合"));
        bodyExtendElementRules.add(SmsRulesUtil.getAcctPasswordRule("密码字段规则检验不符合"));
        return execute(requestMessage, baseContext);
    }

    /**
     * 重写扩展字段的校验:校验账号、密码字段
     * @param body (不能传JSONObject)
     *备注:每个实现类需要自己实现该方法,然后进行扩展字段的校验
     * @param baseHandleContext
     * @param <E>
     * @throws ValidBodyException
     */
    @Override
    public <E> void validBodyExtend(SmsRequest<SmsExtend> body, BaseContext<E> baseHandleContext) throws ValidBodyException {
        try {
            if (Objects.nonNull(baseHandleContext)) {
                baseHandleContext.getBodyExtendElementRules().forEach(rule -> valid(rule, body.getExtend()));
            }
        } catch (RuntimeException e) {
            throw new ValidBodyException(e.getMessage());
        }
    }

    /**
     * 请求参数组装
     * 1、serialSeq=集微的transactionId事务ID 字段
     * 2、扩展字段:acctNo账号、acctPassword密码
     * @param request
     * @param response
     * @param baseContext
     * @throws BeforeOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void beforeOutbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws BeforeOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        String serialSeq = request.getHeader().getSerialSeq();
        try {
           
            log.info("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},集微前置处理:数据落库和参数组装",requestNo,serialSeq);
            List<SendDetailSub> sendDetailSubList = new ArrayList<>();
            String requestParamStr = buildParam(request, sendDetailSubList);

            baseContext.setRestHttpEntity(creatRequestEntity(requestParamStr));
            //保存数据
            saveBatch(requestNo, sendDetailSubList);
            log.info("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},保存集微明细表数据条数:{},前置处理结束", requestNo, serialSeq, sendDetailSubList.size());
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},前置处理异常:{}", requestNo, e.getMessage(), e);
            throw new BeforeOutboundException(e.getMessage(), e);
        }
    }

    /**
     * 外呼请求
     * @param request
     * @param response
     * @param baseContext
     * @throws OutboundException
     */
    @Override
    public void outbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws OutboundException {
        try{
            log.info("短信请求业务号requestNo:{},集微发送短信请求外呼,请求地址:{},请参数:{}",request.getHeader().getRequestNo(),jiweiProperties.getSendDifferentUrl(),baseContext.getRestHttpEntity().getBody());
            String resp = restTemplate.postForObject(jiweiProperties.getSendDifferentUrl(), baseContext.getRestHttpEntity(), String.class);
            log.info("短信请求业务号requestNo:{},集微发送短信请求外呼,响应内容:{}", request.getHeader().getRequestNo(), resp);
            baseContext.setRestResponse(resp);
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},集微发送短信请求外呼异常:{}", request.getHeader().getRequestNo(), e.getMessage());
            throw new OutboundException(e.getMessage(), e);
        }
    }
    /**
     * 后置处理:
     * 根据集微响应的success字段进行判断发送状态:
     * 1、success=true:下行成功(交行成功),终态需要等待回调通知,根据requestNo、transactionId,更新集微明细表数据
     发送状态=>处理中(SmsSendStatusEnum.PROCESSING)
     * 2、success=false:存在部分或者全部失败,会返回失败列表failList,然后根据对方响应的,更新失败的数据
     注意:此时需要把失败列表响应给sms
     发送状态=>失败(SmsSendStatusEnum.FAILURE)
     * 超出范围1、2的判断则置为异常,不更新数据
     * success=true或者false都是 接口交互状态都是:response.setApiStatusEnum(ApiStatusEnum.SUCCESS);
     *
     *4、更新数据规则:
     (1)全部成功时,根据requestNo、batchId 条件 更新本批次全量数据
     (2)存在部分失败时:先根据requestNo、batchId 条件 更新本批次全量数据,后再更新失败的(具体失败的原因)
     表中字段 success 如果时false的值,且 errorCode为空值,则说明本号码是交互成功,但是存在部分失败的号码
     * @param request
     * @param response
     * @param baseContext
     * @throws AfterOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void afterOutbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<String> baseContext) throws AfterOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try{
            log.info("短信请求业务号requestNo:{},后置处理:解析响应结果", requestNo);
            JSONObject restResponse = JSON.parseObject(baseContext.getRestResponse());
            //返回 true表示都下行成功、返回false 表示部分或全部失败
            String success = Optional.ofNullable(restResponse.getString(Constants.RESP_SUCCESS)).orElse("");
            String successDesc = "";
            log.info("短信请求业务号requestNo:{},集微响应的success(成功标志字段)为:{}", requestNo, success);
            JSONObject responseBody = new JSONObject();
            responseBody.put(Constants.RESP_SUCCESS, success);

            //失败记录
            List<SendDetailSub> failSendDetailSubList = new ArrayList<>();
            switch (success) {
                case "true":
                    //全部成功
                    UpdateWrapper<SendDetailSub> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.lambda().set(SendDetailSub::getSuccess, success)
                            .eq(SendDetailSub::getRequestNo, requestNo)
                            .eq(SendDetailSub::getTransactionId, request.getHeader().getSerialSeq());
                    sendDetailSubService.update(new SendDetailSub(), updateWrapper);
                    log.info("短信请求业务号requestNo:{},集微响应success字段:{},根据requestNo、transactionId,更新集微明细表数据", requestNo, success);
                    //有对接对方异步通知接口
                    response.getHeader().setProcessState(ProcessStateEnum.NOTIFICATION);
                    successDesc = "下行成功";
                    break;
                case "false":
                    List<JSONObject> failList = (List<JSONObject>) Optional.ofNullable(restResponse.get(Constants.RESP_FAIL_LIST))
                            .orElseGet(() -> {
                                log.error("短信请求业务号requestNo:{},集微响应的success字段为:{},但是failList失败列表为空",requestNo,success);
                                return Collections.emptyList();
                            });
                    failList.forEach(failItem -> {
                        SendDetailSub sendDetailSub = new SendDetailSub();
                        sendDetailSub.setUuid(failItem.getString(Constants.RESP_FAIL_LIST_UUID));
                        sendDetailSub.setCellPhoneNo(failItem.getString(Constants.RESP_FAIL_LIST_MOBILE));
                        sendDetailSub.setErrorCode(failItem.getString(Constants.RESP_FAIL_LIST_ERROR_CODE));
                        sendDetailSub.setErrorDesc(failItem.getString(Constants.RESP_FAIL_LIST_ERROR_DESC));
                        sendDetailSub.setSuccess(success);
                        failSendDetailSubList.add(sendDetailSub);
                    });
                    responseBody.put(SmsConstants.RESP_FAIL_LIST, failList);
                    response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
                    successDesc = "存在全部或者部分失败";
                    break;
                default:
                    log.error("短信请求业务号requestNo:{},集微返回的success字段的值:{}超出判断范围", requestNo, success);
                    response.getHeader().setProcessState(ProcessStateEnum.EXCEPTION);
                    break;
            }

            //更新数据规则:当(失败数据为空,全部交互成功)或者(存在部分失败数据和发送的数据数量不等时=>就是存在部分失败),则更新全部数据
            if (CollectionUtils.isEmpty(failSendDetailSubList)||request.getBody().getSendDtl().size()!=failSendDetailSubList.size()) {
                updateSendDetailSub(requestNo,request.getHeader().getSerialSeq(),success);
            }
            //如果存在失败记录,则更新失败记录
            if (CollectionUtils.isNotEmpty(failSendDetailSubList)) {
                sendDetailSubService.updateBatchById(failSendDetailSubList);
                log.info("短信请求业务号requestNo:{},集微响应success字段:{},更新明细表(失败号码列表)条数:{}", requestNo, success, failSendDetailSubList.size());
            }
            //如果有失败列表必须返回,在sms模块需要进行处理
            response.getHeader().setResCode(success);
            response.getHeader().setResMessage(successDesc);
            //body里面存 failList
            response.setBody(responseBody);
            //时间在 IOutboundService有设置
            //response.getHeader().setResTime(LocalDateTime.now());

            //response.setErrorCode(success);
            //response.setErrorDesc(successDesc);
            //response.setExtend(responseBody);
            //response.setEndTime(LocalDateTime.now());
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},后置处理异常:{}", requestNo, e.getMessage(), e);
            throw new AfterOutboundException(e.getMessage(), e);
        }
    }


    /**
     * 数据保存
     * @param requestNo
     * @param sendDetailSubList
     */
    private void saveBatch(String requestNo,List<SendDetailSub> sendDetailSubList) {
        try {
            sendDetailSubService.saveBatch(sendDetailSubList);
        } catch (DuplicateKeyException e) {
            log.error("请求流水号requestNo:{},保存集微明细数据,存在重复主键Id,违反唯一约束条件", requestNo);
            throw new RepetitionException(e);
        }
    }



    /**
     * 参数组装
     * @param request
     * @param sendDetailSubList
     */
    private String buildParam(BaseRequest<SmsRequest<SmsExtend>> request, List<SendDetailSub> sendDetailSubList) {
        String requestNo = request.getHeader().getRequestNo();
        String serialSeq = request.getHeader().getSerialSeq();
        SmsRequest<SmsExtend> requestBody = request.getBody();
        
        if (CollectionUtils.isEmpty(requestBody.getSendDtl())){
            log.error("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},请求参数的发送集合是空", requestNo, serialSeq);
            throw new BeforeOutboundException("短信发送集合sendDtl字段为空!");
        }
        JSONObject requestParam = new JSONObject();
        //账号
        requestParam.put(Constants.ACCOUNT, requestBody.getExtend().getAcctNo());

        //事务id(幂等处理使用，要求每次提交的值不相同),等同于标识这批次短信
        requestParam.put(Constants.TRANSACTION_ID, serialSeq);
        //密码=account+pwd+transactionId 按照顺序排列（不包含“+”字符）后,再经过标准 MD5小写加密(其中pwd 为客户密码,服务方提供)
        requestParam.put(Constants.PASSWORD, MD5Util.md5(StringUtils.join(requestBody.getExtend().getAcctNo(), requestBody.getExtend().getAcctPassword(), serialSeq)));
        //list
        List<JSONObject> smsList = new ArrayList<>();
        requestBody.getSendDtl().forEach(messageParamItem -> {
            JSONObject smsListItem = new JSONObject();
            smsListItem.put(Constants.MOBILE, messageParamItem.getCellPhoneNo());
            smsListItem.put(Constants.UUID,messageParamItem.getUuid());
            smsListItem.put(Constants.CONTENT,messageParamItem.getContent());
            smsList.add(smsListItem);
            //实体类
            SendDetailSub sendDetailSub = new SendDetailSub();
            sendDetailSub.setUuid(messageParamItem.getUuid());
            sendDetailSub.setCellPhoneNo(messageParamItem.getCellPhoneNo());
            sendDetailSub.setRequestNo(requestNo);
            sendDetailSub.setTransactionId(serialSeq);
            sendDetailSub.setAccount(requestBody.getExtend().getAcctNo());
            sendDetailSubList.add(sendDetailSub);
        });
        requestParam.put(Constants.LIST, smsList);
        log.info("短信请求业务号requestNo:{},transactionId事务ID(批次号):{},参数组织完成,参数:{}",requestNo, serialSeq, JSON.toJSONString(requestParam));
        return String.valueOf(requestParam);
    }

    private HttpEntity<String> creatRequestEntity(String params) {
        HttpHeaders headers = new HttpHeaders();
        //原先代码有
        /*Map<String, String> mapHeader = new HashMap<>();
        mapHeader.put("Connection","close");
        headers.setAll(mapHeader);*/
        //MediaType是 application/json;charset=UTF-8
        headers.setContentType(MediaType.parseMediaType(Constants.MEDIA_TYPE_APPLICATION_JSON_UTF8));
        return new HttpEntity<>(params, headers);
    }

    /**
     * 更新集微明细数据
     * @param requestNo
     * @param batchId
     * @param success
     */
    private void updateSendDetailSub(String requestNo,String batchId,String success){
        UpdateWrapper<SendDetailSub> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(SendDetailSub::getSuccess, success)
                .eq(SendDetailSub::getRequestNo, requestNo)
                .eq(SendDetailSub::getTransactionId, batchId);
        sendDetailSubService.update(new SendDetailSub(), updateWrapper);
        log.info("短信请求业务号requestNo:{},集微响应success字段:{},根据requestNo、transactionId,更新集微明细表数据", requestNo, success);
    }

 







 /*   @Override
    public JSONObject sendMsgs(SmsRequestMessage<JSONObject> requestMessage) {
        log.info("jw start sendSmsMs,请求报文：{}", JSON.toJSONString(requestMessage));
        JSONObject resultJSONObject = new JSONObject();
        resultJSONObject.put("batchId",requestMessage.getBatchId());
        try{
            List<SmsRequestMessage.ParamInfo> paramInfoList = requestMessage.getSendDtl().stream().collect(Collectors.toList());
            Map<String, Object> accountMap = (Map<String, Object>) jiweiProperties.getAccountMap().get(requestMessage.getSmsMfgChanCd());
            log.info("获取账号信息为：{}", JSON.toJSONString(accountMap));
            if(CollectionUtils.isNotEmpty(paramInfoList)){
                Map<String, String> headers = new HashMap<>();
                headers.put("Connection","close");
                ResponseEntity<String> responseEntity = client
                        .post(jiweiProperties.getSendDifferentUrl(),
                                getRequestBody(requestMessage.getBatchId(), paramInfoList, (String) accountMap.get(Constants.ACCOUNT), (String) accountMap.get(Constants.PASSWORD)).toString(),
                                headers,
                                String.class);
                JSONObject jsonObject = JSON.parseObject(client.responseDataToString(responseEntity));
                log.info("集微响应报文:{}",jsonObject);
                String rst = jsonObject.getBooleanValue(Constants.SUCCESS) ? ResultEnum.SUCCESS.getCode() : ResultEnum.FAIL.getCode();
                String msg = jsonObject.getBooleanValue(Constants.SUCCESS) ? ResultEnum.SUCCESS.getDescription() : ResultEnum.FAIL.getDescription();
                resultJSONObject.put(Constants.RESULT, rst);
                resultJSONObject.put(Constants.MESSAGE, msg);
                if(!jsonObject.getBooleanValue(Constants.SUCCESS)){
                    resultJSONObject.put("failList", jsonObject.get("failList"));
                }
            }else{
                resultJSONObject.put(Constants.RESULT, ResultEnum.EMPTY_DATALIST.getCode());
                resultJSONObject.put(Constants.MESSAGE, ResultEnum.EMPTY_DATALIST.getDescription());
            }
            log.info("jw end sendSmsMsg");
            return resultJSONObject;
        } catch (Exception e) {
            log.error("集微短信发送失败，请求流水号:{},原因:{}", requestMessage.getRequestNo(), e.getMessage());
            resultJSONObject.put(Constants.RESULT, ResultEnum.EXP.getCode());
            resultJSONObject.put(Constants.MESSAGE, ResultEnum.EXP.getDescription());
            return resultJSONObject;
        }
    }*/

   /* private JSONObject getRequestBody(String batchId, List<SmsRequestMessage.ParamInfo> jwParamList, String acc, String pss) {
        JSONObject requestBody = new JSONObject();
        requestBody.put("account", acc);
        requestBody.put("transactionId", batchId);
        requestBody.put("password", MD5Util.md5(acc + pss + batchId));

        List<JSONObject> jsonObjectList = new LinkedList<>();
        for (SmsRequestMessage.ParamInfo paramInfo : jwParamList) {
            String uuid = paramInfo.getUuid();
            String phoneNo = paramInfo.getCellPhoneNo();
            String content = paramInfo.getContent();

            JSONObject oneJSONObject = new JSONObject();
            oneJSONObject.put("mobile",phoneNo);
            oneJSONObject.put("uuid",uuid);
            oneJSONObject.put("content",content);
            jsonObjectList.add(oneJSONObject);
        }

        requestBody.put("list",jsonObjectList);
        log.info("jw requestBody:{}",requestBody);
        return requestBody;
    }*/

}
